/*
 * PBBExpression.java
 *
 * Created on September 7, 2007, 2:35 PM
 *
 * Used to retreive Expression data for the Protein Box Bot.  Uploads Files to Wikipedia if Necessary.
 *
 *Copyright 2007-2008 Jon W Huss III

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
 *
 */

package proteinboxbot;

import java.io.*;
import java.net.*;
import java.util.*;
import proteinboxbot.*;


/**
 *
 * @author Jon Huss
 */
public class PBBExpression {
    
    
    //Hardcoding this stuff in isn't the best idea, but it is the fastest for now.
    public static final String EXPRESSION_IMAGE_URL = "http://biogps.gnf.org/services/CreateChart.aspx";
    
    PBBWikiCom wiki;
    PBBLogging log;
    //String fileDir;
    File sourceDir;
    /** Creates a new instance of PBBExpression */
    public PBBExpression(PBBWikiCom w, PBBLogging l, String fileDir) throws IOException {
        wiki =w; // Entirely possible that this is a null value.
        log = l;
        
        sourceDir = new File(fileDir);
        if (!sourceDir.isDirectory()){
            throw new IOException("Expression_Files does not exist or is not a directory!");
        }
        
    }
    
    public PBBExpression(PBBWikiCom w, PBBLogging l, File fileDir) throws IOException {
        wiki =w; // Entirely possible that this is a null value.
        log = l;
        
        sourceDir = fileDir;
        if (!sourceDir.isDirectory()){
            throw new IOException("Expression_Files does not exist or is not a directory!");
        }
    }
    
    @SuppressWarnings("static-access")
    public Vector<String> getExpressionFiles(String name, PBBdata data) throws PBBWikiException{
        //There are up to three gene epression thumbnails and pic for each protein (well,
        //there may be more but we ignore any extras). We need to get the potential names
        //from the data, then we need to look on wikipedia and see if those names already exist.
        //if they do, then we don't need to upload anything new. and we just use the pre-existing
        //files. If they don't then we need to look and retreive the file data (from some source).
        //then we need to upload the new data to wikipedia.
        
        //At the end of it all, we will return the filenames of what we found. These
        //files names should then be added back into data for the wikicoding later.
        
        //Hardcoded wiki names:  PBB_GE_<name>_<filecode>.png
        //Filecodes come from the XML file and are listed in dataVals.
        //Name is an input to this method and should be the symbol for the protein.
        log.addEntry("SYSTEM","EXPRESSION","Getting Expression files for protein: " + name, PBBLogging.logs.SY);
        String[] dataVals = data.getValueList("Expression_Files");
        
        Vector<String> fileNamesThumb = new Vector<String>();
        //Vector<String> fileNamesFull = new Vector<String>();
        
        
        for (int i = 0; i< dataVals.length; i++){
            String id = dataVals[i];
            String fileThumb = id + "_tn.png";
            String fileFull = id + "_fs.png";
            String properFileThumb = "PBB_GE_"+name+"_"+fileThumb;
            String properFileFull = "PBB_GE_"+name+"_"+fileFull;
            
            if (wiki != null){
                //Only check wikipedia for the files if we actually have a wiki object.
                //This is likely to be null if wiki communication is set to false.
                if (wiki.wikiHasImage("Image:" + properFileThumb)){
                    //Already there, we just put the value in the vector and move on..
                    log.addEntry("SYSTEM","EXPRESSION","Image already exists on wiki:" + properFileThumb, PBBLogging.logs.SY);
                    fileNamesThumb.add(properFileThumb);
                    //fileNamesFull.add(properFileFull);
                    continue; //next image!
                }
            }
            
            //Ok, looks like we haven't found the file yet.. lets keep looking...
            byte[] imageThumb = getImage(fileThumb,properFileThumb,id,"tn");
            byte[] imageFull = getImage(fileFull,properFileFull,id,"fs");
            
            if (imageThumb != null){
                //Good we have an Image!
                //Now we only care about uploading that image if we are communicating with the Wiki..
                
                if (wiki != null){
                    
                    //Now we will want to check to see if a full size image exists, and upload both if they do.
                    
                    if (imageFull != null){
                        //We have both! - so lets upload both of them.
                        //Check to make sure that the full file isn't already there...
                        if (!wiki.wikiHasImage("Image:" + properFileFull)){
                            //Full image isn't there so we will upload a new one.
                            log.addEntry("SYSTEM","EXPRESSION","Attempting to upload image to wiki:" + properFileFull , PBBLogging.logs.SY);
                            
                            wiki.uploadImage(properFileFull,imageFull,wiki.geneExpImageCopyrightFull(name));
                        }
                        
                        //now upload the thumbnail.
                        wiki.uploadImage(properFileThumb,imageThumb,wiki.geneExpImageCopyrightThumb(name,properFileFull));
                        
                    } else {
                        //just upload the thumbnail..
                        wiki.uploadImage(properFileThumb,imageFull,wiki.geneExpImageCopyrightThumb(name,properFileFull));
                        
                    }
                    
                }
                fileNamesThumb.add(properFileThumb);
                continue;
            }
            
            
        }
        
        
        
        return fileNamesThumb;
    }
    
    private String getExpPage(String id, String size) {
        //Returns the location URL for an expression file.
        if (size.compareTo("fs") != 0 && size.compareTo("tn") !=0){
            //default to thumbnail size if the size option is bad.
            size = "tn";
        }
        return PBBExpression.EXPRESSION_IMAGE_URL + "?probeset_id="+id+"&dataset=HG_U133A&type="+size+"&o=h";
    }
    
    
    /**
     *
     * @param id
     * @param size
     * @return
     */
    private byte[] getImageFromServer(String id, String size){
        
        
        //wraps for error catching.
        boolean clear = false;
        int tries = 0;
        while (!clear){
            
            try {
                byte[] data = this.getImageFromServerH(this.getExpPage(id,size));
                return data;
            } catch(Exception e) {
                System.out.println("Expression: Encountered Error while Downloading Image:");
                System.out.println(e.getMessage());
                if (tries < 3){
                    System.out.println("Trying "+ (3 - tries) + " more time(s) in 10 seconds...");
                    this.updateWait(10000);
                    this.allowComm();
                    tries++;
                    
                    
                } else {
                    System.out.println("3 tries failed; Requiring User Input:");
                    tries = 0;
                    try {
                        BufferedReader inputLine = new BufferedReader(new InputStreamReader(System.in));
                        System.out.println("Try Again? (y/n)");
                        System.out.flush();
                        String input = inputLine.readLine();
                        //input = "y";
                        if (input.compareToIgnoreCase("y") == 0){
                            clear= false;
                        }else {
                            clear = true;
                            System.out.println("Image download aborted by user, returning null value.");
                            return null;
                            //throw new PBBWikiException("User Aborted Download due to error.",e);
                            
                        }
                        
                    } catch (IOException ex) {
                        //had a bad error - that is ok, we'll just return a null value.
                        return null;
                        //throw new PBBWikiException("Unrecoverable IO Error.",ex);
                        //System.out.println("Unrecoverable IO Error.");
                        //ex.printStackTrace();
                        //System.exit(1);
                    }
                }
            }
        }
        
        //throw new PBBWikiException("Unknown Download Error.");
        return null;
    }
    
    
    /**
     * Helper method for getImageFromServer.  Does the actual Downloading.
     *
     * @param id
     * @param size
     * @return null if no data is present, or an array of bytes of image data.
     */
    private byte[] getImageFromServerH(String page) throws Exception{
        String lineSep = System.getProperty("line.separator");
        
        URL expURL;
        URLConnection expConn;
        
        expURL = new URL(page);
        log.addEntry("SYSTEM","EXPCOMM","Downloading File:"+ expURL.toString(),PBBLogging.logs.SY);
        
        expConn = expURL.openConnection();
        //first thing we have to grab is the html page that will come back at us, that will have
        //an image link for us to grab..
        BufferedReader expBR = new BufferedReader(new InputStreamReader(expConn.getInputStream(),"UTF-8"));
        
        //BufferedWriter bw = new BufferedWriter(new FileWriter(destination));
        String inLine = "";
        String imageLocation = "";
        while ( (inLine = expBR.readLine()) != null) {
            if (inLine.indexOf("<img") != -1){
                //got the image line.. now parse it.
                int index1 = inLine.indexOf("src=\"");
                int index2 = inLine.indexOf("\"",index1+"src=\"".length());
                imageLocation = inLine.substring(index1+"src=\"".length(),index2);
            }
            
        }
        
        if (imageLocation.trim().compareTo("") == 0){
            //found nothing
            return null;
        }
        //imageLocation = expURL.getProtocol()+ "://" + expURL.getHost() + imageLocation;
        URL imgURL = new URL(expURL.getProtocol(), expURL.getHost(),imageLocation);
        expConn = imgURL.openConnection();
        
        byte[] input = new byte[1024];
        InputStream expIs = expConn.getInputStream();
        
        int numRead;
        byte[] fullData = new byte[0];
        byte[] tmp = null;
        while ( (numRead = expIs.read(input)) != -1) {
            tmp = fullData;
            fullData = new byte[tmp.length+numRead];
            System.arraycopy(tmp,0,fullData,0,tmp.length);
            System.arraycopy(input,0,fullData,tmp.length,numRead);
        }
        if (fullData.length < 1) {
            //we return null when no data is present.
            fullData = null;
        }
        
        return fullData;
        
    }
    
    
    /**
     * returns Null if the image does not exist...
     *
     *   at the moment, we are only going to look on the local computer for the file, later
     *   we may expand this to look up the file on a server somewhere..
     *
     *
     *   Update: we are now going to look on a server first.  If we don't find anything,
     *   then we will look locally.
     *
     * @param name
     * @param properName
     * @param id
     * @param size
     * @return
     */
    private byte[] getImage(String name, String properName, String id, String size){
        
        byte[] data = this.getImageFromServer(id,size);
        if (data != null) {
            return data;
        }
        
        //didn't find an image on the server, so now we'll look locally.
        File imageFile = new File(sourceDir,name);
        try {
            
            if (!imageFile.canRead()) {
                return null;
            }
            FileInputStream fis = new FileInputStream(imageFile);
            byte[] input = new byte[8096];
            
            int numRead;
            byte[] fullData = new byte[0];
            byte[] tmp = null;
            while ( (numRead = fis.read(input)) != -1) {
                tmp = fullData;
                fullData = new byte[tmp.length+numRead];
                System.arraycopy(tmp,0,fullData,0,tmp.length);
                System.arraycopy(input,0,fullData,tmp.length,numRead);
            }
            if (fullData.length > 0){
                log.addEntry("SYSTEM","EXPRESSION","Found Image on Disk:" + name , PBBLogging.logs.SY);
                return fullData;
            }
            
        } catch (Exception ex) {
            //if anything goes wrong, we'll just assume the file isn't there..
            return null;
        }
        
        return null;
    }
    
    private Date nextComm;
    
    private void updateWait(int wait){
        Date now = new Date();
        nextComm = new Date(now.getTime() + wait);
        //DEBUG
        //System.out.println("NextComm = " + nextComm.getTime());
        //System.out.println("Now = " + now.getTime());
        //System.out.println("Difference = " + (nextComm.getTime() - now.getTime())+ " should be ~" + wait);
        
        
        
    }
    private void allowComm() {
        //this function makes the bot wait until it is allowed to perform the next communication with Wiki.
        Date now = new Date();
        
        long wait = nextComm.getTime() - now.getTime();
        //System.out.println("Wait = " + wait);
        double waitTime = ((double)wait)/1000;
        if (wait > 0) {
            System.out.println("Citation: Waiting " + waitTime + " seconds for cooldown.");
            System.out.flush();
            long left = wait;
            while( left > 10){
                System.out.print(".");
                System.out.print(" ");
                System.out.flush();
                try {
                    if( left > 1000){
                        Thread.currentThread().sleep(1000);
                        left = left - 1000;
                    }else {
                        Thread.currentThread().sleep(left);
                        left = 0;
                        
                    }
                } catch (InterruptedException ex) {
                    System.out.println("Interrupted While Sleeping. Non-hazardous Error (ignored).");
                    //ex.printStackTrace();
                }
                
                
            }
            Date time2 = new Date();
            long actWait = time2.getTime() - now.getTime();
            System.out.println(" ("+ actWait + " milliseconds.)");
            //glblLog.addEntry("SYSTEM","SLEPT","Citation Waited " + actWait + " milliseconds for Cooldown.",PBBLogging.logs.SY);
            
        }
        return;
        
    }
}
